/**
 * 判断传入参数可否转化为Date对象
 * @since 1.0.0
 * @category DateUtils
 * @param date 需判断的参数
 * @return 返回布尔值
 * @example
 * ```javascript
 * const date = new Date()
 * isDate(date)
 * // => true
 * const dateInvalid = new Date('778899')
 * isDate(dateInvalid)
 * // => false
 * const notDate = '1649988990749' // string
 * isDate(notDate)
 * // => false
 * const time = 1649988990749   // number
 * isDate(time)
 * // => false
 * ```
 */
export const isDate = (date: unknown) => (date instanceof Date) && !isNaN(date.getTime());

/**
 * 判断是否为闰年
 * @since 1.0.0
 * @category DateUtils
 * @param date
 * @return
 * @example
 * ```js
 * const date = new Date('2020/1/1')
 * isLeapYear(date)
 * // => return true
 * const notLeapYear = new Date('2500/2/1')
 * isLeapYear(notLeapYear)
 * // => return false
 * const notDate = new Date('2500/111/1')
 * isLeapYear(notDate)
 * // => return null
 * ```
 */
export const isLeap = (date = new Date()) => (date.getFullYear() % 400 === 0) || (date.getFullYear() % 4 === 0 && date.getFullYear() % 100 !== 0)

/**
 * 转化为Date对象
 * @since 1.0.0
 * @category DateUtils
 * @todo 需要在lang工具类中新增一个纯数字字符串转数字的方法
 * @param {Date} param
 * @return {Date | null}
 * @example
 * ```js
 * toDate('2021/12/12');
 * // => return Sun Dec 12 2021 00:00:00 GMT+0800 (中国标准时间)
 * toDate(1639238400000)
 * // => return Sun Dec 12 2021 00:00:00 GMT+0800 (中国标准时间)
 * toDate('1639238400000')
 * // 注：new Date() 不接收'1640332360164'这样的数据，需要转换一遍
 * // => return Sun Dec 12 2021 00:00:00 GMT+0800 (中国标准时间)
 * toDate('sds');
 * // => return null;
 * ```
 */
export function toDate(param: any): Date | null {
  const num = parseInt(param);
  if (num == param) {
    const date = new Date(num);
    return isDate(date) ? date : null;
  } else {
    const date = new Date(param);
    return isDate(param) ? date : null;
  }
}

function setDateParam(
  date: Date = new Date(),
  type: string = "second",
  value: number = 60,
  after: boolean = true
): Date | null {
  if (isDate(date)) {
    let d = new Date(date);
    switch (type) {
      case "y":
        after
          ? d.setFullYear(d.getFullYear() + value)
          : d.setFullYear(d.getFullYear() - value);
        return new Date(d);
      case "q":
        after
          ? d.setMonth(d.getMonth() + 3 * value)
          : d.setMonth(d.getMonth() - 3 * value);
        return new Date(d);
      case "m":
        after
          ? d.setMonth(d.getMonth() + value)
          : d.setMonth(d.getMonth() - value);
        return new Date(d);
      case "d":
        after ? d.setDate(d.getDate() + value) : d.setDate(d.getDate() - value);
        return new Date(d);
      case "H":
        after
          ? d.setHours(d.getHours() + value)
          : d.setHours(d.getHours() - value);
        return new Date(d);
      case "M":
        after
          ? d.setMinutes(d.getMinutes() + value)
          : d.setMinutes(d.getMinutes() - value);
        return new Date(d);
      case "S":
        after
          ? d.setSeconds(d.getSeconds() + value)
          : d.setSeconds(d.getSeconds() - value);
        return new Date(d);
      case "MS":
        after ? d.setTime(d.getTime() + value) : d.setTime(d.getTime() - value);
        return new Date(d);
      default:
        console.error(
          "type不在y,q,m,d,H,M,S,MS（年，月，日，时，分，秒，毫秒）中"
        );
        return null;
    }
  }
  return null;
}

/**
 * 获取指定时间后的日期， 默认一分钟
 * @since 1.0.0
 * @category DateUtils
 * @param {Date = new Date()} date
 * @param {string = 'second'} type
 * @param {number = 60} value
 * @return {Date | null}
 * @example
 * ```js
 * const date = new Date('2022/1/1 12:00:00')
 * setAfterDate(date)
 * // => return new Date('2022/1/1 12:01:00')
 * const date = new Date('2022/1/1 12:00:00')
 * setAfterDate(date, 'year', 1)
 * // => return new Date('2023/1/1 12:00:00')
 * const date = new Date('2022/13/1 12:00:00')
 * setAfterDate(date, 'year', 1)
 * // => return null
 * ```
 */
export function setAfterDate(
  date: Date = new Date(),
  type: string = "second",
  value: number = 60
): Date | null {
  return setDateParam(date, type, value, true);
}

/**
 * 获取指定时间前的日期，默认一分钟
 * @since 1.0.0
 * @category DateUtils
 * @param {Date = new Date()} date
 * @param {string = 'second'} type
 * @param {number = 60} value
 * @return {Date | null}
 * @example
 * ```js
 * const date = new Date('2022/1/1 12:00:00')
 * setBeforeDate(date)
 * // => return new Date('2022/1/1 11:59:00')
 * const date = new Date('2022/1/1 12:00:00')
 * setBeforeDate(date, 'year', 1)
 * // => return new Date('2021/1/1 12:00:01')
 * const date = new Date('2022/13/1 12:00:00')
 * setBeforeDate(date, 'year', 1)
 * // => return null
 * ```
 */
export function setBeforeDate(
  date: Date = new Date(),
  type: string = "second",
  value: number = 60
): Date | null {
  return setDateParam(date, type, value, false);
}

/**
 * 对比两个时间，返回差值，有正负
 * @since 1.0.0
 * @category DateUtils
 * @param {Date} startDate 开始时间
 * @param {Date} endDate 结束时间
 * @return {number} 单位：毫秒
 * @example
 * ```js
 * const m = new Date('2021/12/12');
 * const n = new Date('2021/12/15');
 * compareDate(m, n);
 * // return 259200000
 * ```
 */
export function compareDate(startDate: Date, endDate: Date): number | null {
  if (isDate(startDate) && isDate(endDate)) {
    return endDate.getTime() - startDate.getTime();
  }
  return null;
}

/**
 * 格式化时间
 * @since 1.0.0
 * @category DateUtils
 * @param {Date = new Date()} param
 * @param {string = 'yyyy/mm/dd HH:MM:SS'} type
 * @return {string | Null}
 * @example
 * ```js
 * formatDate(new Date(), 'yyyy-mm')
 * // return 2021-12
 * ```
 */
export function formatDate(
  param: Date = new Date(),
  type: string = "yyyy/mm/dd HH:MM:SS"
): string | null {
  const date = toDate(param);
  if (date === null) return null;
  // let ret;
  const opt: { [index: string]: string } = {
    "y+": date.getFullYear().toString(), // 年
    "m+": (date.getMonth() + 1).toString(), // 月
    "d+": date.getDate().toString(), // 日
    "H+": date.getHours().toString(), // 时
    "M+": date.getMinutes().toString(), // 分
    "S+": date.getSeconds().toString(), // 秒
  };
  for (let k in opt) {
    let ret = new RegExp("(" + k + ")").exec(type);
    if (ret) {
      type = type.replace(
        ret[1],
        ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
      );
    }
  }
  return type;
}

/**
 * 获取某月天数,默认当月
 * @since 1.0.0
 * @category DateUtils
 * @param {Date = new Date()} date 日期
 * @return {number | null}
 * @example
 * ```js
 * const date = new Date('2021/12/12');
 * getYear(date);
 * // return 31
 * ```
 */
export function getDaysOfMonth(date: Date = new Date()): number | null {
  if (isDate(date)) {
    const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
    return temp.getDate();
  }
  return null;
}

/**
 * 获取一年中总天数
 * @param date 
 * @returns 总天数
 */
export const getDaysOfYear = (date = new Date()) => isLeap(date) ? 366 : 365;

const weekDesc = [
  "星期天",
  "星期一",
  "星期二",
  "星期三",
  "星期四",
  "星期五",
  "星期六",
];

/**
 * @interface WeekDescInterface 描述周几的数据结构
 * @param {number} day 具体周几
 * @param {string} desc 中文描述
 */
export interface WeekDescInterface {
  day: number;
  desc: string;
}

/**
 * 获取传入时间是周几
 * @since 1.0.0
 * @category DateUtils
 * @param {Date = new Date()} date
 * @return {WeekDescInterface | null}
 * @example
 * ```js
 * const date = new Date('2022/4/15')
 * getWeekDescription(date)
 * // => return {day: 5, desc: '星期五'}
 * const date = new Date('2022/433/15')
 * getWeekDescription(date)
 * // => return null
 * ```
 */
export function getWeekDescription(
  date: Date = new Date()
): WeekDescInterface | null {
  if (isDate(date)) {
    const day = date.getDay();
    // 星期日是0
    return {
      day: day,
      desc: weekDesc[day],
    };
  }
  return null;
}
